Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace provision Tagging tree by component #5550

Merged
merged 13 commits into from
Aug 5, 2019

Conversation

PanSpagetka
Copy link
Contributor

@PanSpagetka PanSpagetka commented May 9, 2019

Replace provision Tagging tree with a new component

Links [Optional]

#5515

Screenshot

Screenshot from 2019-07-29 15-35-29

Steps for Testing/QA [Optional]

Provision VM/Instance -> Purpose tab

@PanSpagetka
Copy link
Contributor Author

@miq-bot add_label WIP

@miq-bot miq-bot added the wip label May 9, 2019
@PanSpagetka PanSpagetka changed the title [WIP] Replace provision Tagging try by component [WIP] Replace provision Tagging tree by component May 9, 2019
@PanSpagetka PanSpagetka force-pushed the provision-tagging-replace branch 3 times, most recently from b0f1698 to 3f7100c Compare May 22, 2019 13:40
@PanSpagetka
Copy link
Contributor Author

@miq-bot add_label tagging

@PanSpagetka PanSpagetka force-pushed the provision-tagging-replace branch from 3f7100c to 4e71039 Compare June 10, 2019 09:35
@PanSpagetka
Copy link
Contributor Author

@miq-bot remove_label wip

@miq-bot miq-bot changed the title [WIP] Replace provision Tagging tree by component Replace provision Tagging tree by component Jun 10, 2019
@miq-bot miq-bot removed the wip label Jun 10, 2019
Copy link
Member

@skateman skateman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have a few antipatterns, please address them.

const params = {id: store.getState().tagging.appState.affectedItems[0], cat: tag.tagCategory.id, val: tag.tagValue.id, check: 1, tree_typ: 'tags' };
const ids = [...store.getState().tagging.appState.assignedTags.map( t => t.values.map(val => val.id)), tag.tagValue.id].flat();
console.log(ids);
const params = (meta.type === 'provision'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a 🦛 step backwards 🦛 with the ternary operator... 🔴 🏴 @himdel @martinpovolny @Hyperkid123 any idea how else we could do this?

Copy link
Contributor

@Hyperkid123 Hyperkid123 Jun 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by step back?

There are not many options with expression like this. You can do classic if statement. Or switch or maybe invoke a function like this:

// or extract it somewhere as a helper function
params = ((type, state) => ({
  provision: state.something...,
  default: 'b'
})[type || 'default'])(meta.type, state)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My problem here is the special case when the tagging is on the provisioning screen. Anything like this has the potential to grow into a huge case with a ton of exceptions

Copy link
Contributor

@Hyperkid123 Hyperkid123 Jun 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok, i would suggest then to add a Prop to components that are using this and create the params inside the component and send it as a (meta)parameter to the action. We have the thunk middleware so we can get to the current state inside action creators almost exactly the same way as in middleware.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a convention for this.

Every form not just talking to the API will have the same problem - the definiton of what to send via http can not be part of the component itself.

Passing a function to transform state to request data sounds reasonable, but maybe we should really do it the same way in all the react forms.

@@ -5,7 +5,12 @@ import promiseMiddleware from 'redux-promise-middleware';
export const taggingMiddleware = store => next => action => {
const { type, meta, tagCategory, tag } = action;
if (meta && meta.url) {
const params = {id: store.getState().tagging.appState.affectedItems[0], cat: tag.tagCategory.id, val: tag.tagValue.id, check: 1, tree_typ: 'tags' };
const ids = [...store.getState().tagging.appState.assignedTags.map( t => t.values.map(val => val.id)), tag.tagValue.id].flat();
console.log(ids);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not wanted

end

assignments = Classification.select { |tag| vm_tags.include?(tag.id) }
assigned_tags = Classification.select { |tag| vm_tags.include?(tag.id) }.map do |tag|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you duplicating code here?

@ci_node[:children] = @ci_kids if @ci_kids.present?
all_tags.push(@ci_node) if @ci_kids.present?
# for some reason @tags is set in wf, and it is changed by map bellow which causes bugs
wf.instance_variable_set(:@tags, nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's wf? If it is undocumented behavior, please look it up and document it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stands for workflow, used a lot in automate code (309 occurences)

:save_url => '',
:cancel_url => ''
}
@tags1 = {:tags => tags, :assignedTags => assigned_tags, :affectedItems => []}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why @tags1? Why not 42?

end
@all_tags_tree = TreeBuilder.convert_bs_tree(all_tags).to_json # Add ci node array to root of tree
tags = tags.map { |t| {:id => t[:name], :singleValue => t[:single_value] }.merge(t) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have better hash manipulation techniques for this, check out the documentation

end
@all_tags_tree = TreeBuilder.convert_bs_tree(all_tags).to_json # Add ci node array to root of tree
tags = tags.map { |t| {:id => t[:name], :singleValue => t[:single_value] }.merge(t) }
tags.map do |t|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not very happy about the map here, maybe each. Or map! and do the delete properly.

@PanSpagetka PanSpagetka force-pushed the provision-tagging-replace branch 5 times, most recently from 380c263 to e198f1d Compare June 11, 2019 11:11
:locals => {:tree_id => "all_tags_treebox",
:tree_name => "all_tags_tree",
:bs_tree => @all_tags_tree,
:oncheck => "miqOnCheckProvTags",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also remove the miqOnCheckProvTags function please (and from the whitelist)?

(If it was used miq_prov_vars would still need the split .. but it is dead now :).)

# for some reason @tags is set in wf, and it is changed by map bellow which causes bugs
wf.instance_variable_set(:@tags, nil)
tags = wf.allowed_tags
tags.each do |cat|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like

tags = wf.allowed_tags.map do |cat|
  {
    :values => cat[:children].map do |tag|
      {:id => tag.first, :description => tag.second[:description]}
    end,
    :id => cat[:name],
    :singleValue => cat[:single_value],
  }
end

would probably be better than overwriting items in the allowed_tags array?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general please prefer functional style over each :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I was stuck with an idea, that I need to modify existing hash....while create a new is much nicer

end.uniq

@button_urls = {
:save_url => '',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks suspicious.. you're never saving or cancelling?

(Maybe just a comment why?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't be there...

@@ -5,10 +5,11 @@ import promiseMiddleware from 'redux-promise-middleware';
export const taggingMiddleware = store => next => action => {
const { type, meta, tagCategory, tag } = action;
if (meta && meta.url) {
Copy link
Contributor

@himdel himdel Jun 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

switch (type) {
  case 'UI-COMPONENTS_TAGGING_TOGGLE_TAG_VALUE_CHANGE':
    $.post(meta.url, meta.params(meta.type, store.getState(), tag));
    break;
  case 'UI-COMPONENTS_TAGGING_DELETE_ASSIGNED_TAG':
    $.post(meta.url, meta.onDelete(meta.type, params, tag.tagValue.id));
    break;
}

and move that check: 0 to onDelete definition?

Copy link
Contributor

@himdel himdel Jun 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or ideally call the same method, since you're passing the type anyway. (type != meta.type, sorry)

The point is, there should be only 1 place dealing with this logic, not 2, or 3.

(maybe onDelete should not take computed params, and do it by itself, from state, that way maybe you can also unify the args)

@skateman
Copy link
Member

@PanSpagetka please avoid using commit messages that lack context, having the "code styling" commit in the master's git log will be totally useless for the future 😕

@martinpovolny
Copy link
Member

ping @terezanovotna

@@ -290,26 +289,20 @@
= field_hash[:notes]
- elsif [:tag_ids, :vm_tags].include?(field)
-# tree control for tags fields
.col-md-8
.col-md-10
#all_tags_treebox.treeview-pf-hover.treeview-pf-select{:style => "color:#000; overflow: hidden;"}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably remove these tree classes and style overrides since this is no longer a tree.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the div is actually empty, and the code setting check is not used anymore, so you can just remove lines 293 to 299

@himdel
Copy link
Contributor

himdel commented Jul 30, 2019

3 more remnants of the tree:

app/views/miq_request/_prov_field.html.haml
293:          #all_tags_treebox.treeview-pf-hover.treeview-pf-select{:style => "color:#000; overflow: hidden;"}

app/views/miq_request/_prov_edit.html.haml
8:  miqDeleteTreeCookies('all_tags_tree')

app/controllers/vm_common.rb
1196:        presenter[:clear_tree_cookies] = "all_tags_tree"

@himdel
Copy link
Contributor

himdel commented Jul 30, 2019

Also looks like this will still need a react-ui-components update:

Warning: Failed prop type: Invalid prop `tags.tags[0].id` of type `string` supplied to `TaggingWrapper`, expected `number`.

Warning: Failed prop type: Invalid prop `tags[0].id` of type `string` supplied to `Tagging`, expected `number`.

Warning: Failed prop type: Invalid prop `tagCategories[0].id` of type `string` supplied to `CategoryModifier`, expected `number`.

Warning: Failed prop type: Invalid prop `tagCategories[0].id` of type `string` supplied to `TagSelector`, expected `number`.

(I don't think the component should ever expect number for id)

@himdel
Copy link
Contributor

himdel commented Jul 30, 2019

It's currently impossible to remove the last tag:

20190730125017 mp4

(it just reappears after the server responds)

@himdel
Copy link
Contributor

himdel commented Jul 30, 2019

When changing a tag from one of the "only a single value" categories:

  1. chose Category > Foo
    the UI shows Category: Foo on the right, sends
id: new
ids_checked[]: 86
tree_typ: tags
check: 0
  1. change to Category > Bar

the UI shows Category: Bar on the right, sends

id: new
ids_checked[]: 86
ids_checked[]: 87
tree_typ: tags

Note that the first tag id remained in the second request, so we're sending 2 tags from a "single tag only" category.

EDIT: this works in conjunction with the backend discarding all but the last id in each single-value category .. and the last chosen will be last simply because it's added to the list last .. and the client state gets updated by the server state on every change. I think this works :)

@PanSpagetka PanSpagetka force-pushed the provision-tagging-replace branch from 4f868a0 to 4b2a2f8 Compare July 31, 2019 09:38
@PanSpagetka PanSpagetka force-pushed the provision-tagging-replace branch from 4b2a2f8 to b467f54 Compare July 31, 2019 13:05
@himdel
Copy link
Contributor

himdel commented Aug 1, 2019

(
Still 2 bugs:

  • sending ids_checked[]: 87 in one request and then ids_checked[]: 87 ; ids_checked[]: 86 in the other (both in same cateogry), 87 remains checked

  • sending ids_checked[]: 87 ; ids_checked[]: 86 in one request and then deleted: 86 will set 87 (instead of none), after that, deleted: 87 goes back to showing 86 (that's because build_tags_tree doesn't mutate @edit[:new][:vm_tags] but only a local copy, so [87,86] remain there when deleting

)

@PanSpagetka PanSpagetka force-pushed the provision-tagging-replace branch from b467f54 to 1bd910a Compare August 1, 2019 11:00
@@ -1013,7 +1013,7 @@ def build_tags_tree(wf, vm_tags, edit_mode)
end
}
end.uniq

# assigned_tags.map! { |c| c[:values].delete_if {|v| v[:id] == params[:deleted].to_i} }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably want to delete this?

@@ -21,7 +21,7 @@ const params = (type = 'default', state, tag = {}) => ({
})[type]

const onDelete = (type = 'default', params = [], deleted_element) => ({
provision: {...params, check: 0, ids_checked: params.ids_checked.filter(element => element !== deleted_element)},
provision: {...params, check: 0, ids_checked: params.ids_checked.filter(element => element !== deleted_element), deleted: deleted_element },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess sending deleted no longer matters?

@himdel
Copy link
Contributor

himdel commented Aug 1, 2019

The spec failures look relevant, the post is called with different params now so the test is wrong.

@miq-bot
Copy link
Member

miq-bot commented Aug 5, 2019

Checked commits PanSpagetka/manageiq-ui-classic@f7abada~...2ce52a3 with ruby 2.4.6, rubocop 0.69.0, haml-lint 0.20.0, and yamllint 1.10.0
3 files checked, 0 offenses detected
Everything looks fine. ⭐

@himdel himdel merged commit 42718a6 into ManageIQ:master Aug 5, 2019
@himdel
Copy link
Contributor

himdel commented Aug 5, 2019

LGTM, merging :)

@PanSpagetka One thing that needs fixing - as a separate PR - we should never be using numeric ids in javascript.

Due to the limitation of MAX_SAFE_INTEGER in JS (see #1405 for details), we need to consistently treat any database id as a string in JS.

Can you change that plase?

@himdel himdel self-assigned this Aug 5, 2019
@himdel himdel added this to the Sprint 117 Ending Aug 5, 2019 milestone Aug 5, 2019
@martinpovolny
Copy link
Member

Great!

Are there any further places where we need the new tagging component?

@skateman
Copy link
Member

skateman commented Aug 5, 2019

Yes, two of them, @PanSpagetka already working on them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants